/******************************************************************************
Module:  SchedLab.cpp
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
******************************************************************************/


#include "..\CommonFiles\CmnHdr.h"     /* See Appendix A. */
#include <windowsx.h>
#include <tchar.h>
#include "Resource.h"
#include <StrSafe.h>


///////////////////////////////////////////////////////////////////////////////


DWORD WINAPI ThreadFunc(PVOID pvParam) {

	HANDLE hThreadPrimary = (HANDLE)pvParam;
	SuspendThread(hThreadPrimary);
	chMB(
		"The Primary thread is suspended.\n"
		"It no longer responds to input and produces no output.\n"
		"Press OK to resume the primary thread & exit this secondary thread.\n");
	ResumeThread(hThreadPrimary);
	CloseHandle(hThreadPrimary);

	// To avoid deadlock, call EnableWindow after ResumeThread.
	EnableWindow(
		GetDlgItem(FindWindow(NULL, TEXT("Scheduling Lab")), IDC_SUSPEND),
		TRUE);
	return(0);
}


///////////////////////////////////////////////////////////////////////////////


BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam) {

	chSETDLGICONS(hWnd, IDI_SCHEDLAB);

	// Initialize process priority classes
	HWND hWndCtl = GetDlgItem(hWnd, IDC_PROCESSPRIORITYCLASS);

	int n = ComboBox_AddString(hWndCtl, TEXT("High"));
	ComboBox_SetItemData(hWndCtl, n, HIGH_PRIORITY_CLASS);

	// Save our current priority class
	DWORD dwpc = GetPriorityClass(GetCurrentProcess());

	if (SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS)) {

		// This system supports the BELOW_NORMAL_PRIORITY_CLASS class

		// Restore our original priority class
		SetPriorityClass(GetCurrentProcess(), dwpc);

		// Add the Above Normal priority class
		n = ComboBox_AddString(hWndCtl, TEXT("Above normal"));
		ComboBox_SetItemData(hWndCtl, n, ABOVE_NORMAL_PRIORITY_CLASS);

		dwpc = 0;  // Remember that this system supports below normal
	}

	int nNormal = n = ComboBox_AddString(hWndCtl, TEXT("Normal"));
	ComboBox_SetItemData(hWndCtl, n, NORMAL_PRIORITY_CLASS);

	if (dwpc == 0) {

		// This system supports the BELOW_NORMAL_PRIORITY_CLASS class

		// Add the Below Normal priority class
		n = ComboBox_AddString(hWndCtl, TEXT("Below normal"));
		ComboBox_SetItemData(hWndCtl, n, BELOW_NORMAL_PRIORITY_CLASS);
	}

	n = ComboBox_AddString(hWndCtl, TEXT("Idle"));
	ComboBox_SetItemData(hWndCtl, n, IDLE_PRIORITY_CLASS);

	ComboBox_SetCurSel(hWndCtl, nNormal);

	// Initialize thread relative priorities
	hWndCtl = GetDlgItem(hWnd, IDC_THREADRELATIVEPRIORITY);

	n = ComboBox_AddString(hWndCtl, TEXT("Time critical"));
	ComboBox_SetItemData(hWndCtl, n, THREAD_PRIORITY_TIME_CRITICAL);

	n = ComboBox_AddString(hWndCtl, TEXT("Highest"));
	ComboBox_SetItemData(hWndCtl, n, THREAD_PRIORITY_HIGHEST);

	n = ComboBox_AddString(hWndCtl, TEXT("Above normal"));
	ComboBox_SetItemData(hWndCtl, n, THREAD_PRIORITY_ABOVE_NORMAL);

	nNormal = n = ComboBox_AddString(hWndCtl, TEXT("Normal"));
	ComboBox_SetItemData(hWndCtl, n, THREAD_PRIORITY_NORMAL);

	n = ComboBox_AddString(hWndCtl, TEXT("Below normal"));
	ComboBox_SetItemData(hWndCtl, n, THREAD_PRIORITY_BELOW_NORMAL);

	n = ComboBox_AddString(hWndCtl, TEXT("Lowest"));
	ComboBox_SetItemData(hWndCtl, n, THREAD_PRIORITY_LOWEST);

	n = ComboBox_AddString(hWndCtl, TEXT("Idle"));
	ComboBox_SetItemData(hWndCtl, n, THREAD_PRIORITY_IDLE);

	ComboBox_SetCurSel(hWndCtl, nNormal);

	Edit_LimitText(GetDlgItem(hWnd, IDC_SLEEPTIME), 4);   // Maximum of 9999
	return(TRUE);
}


///////////////////////////////////////////////////////////////////////////////


void Dlg_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) {

	switch (id) {
	case IDCANCEL:
		PostQuitMessage(0);
		break;

	case IDC_PROCESSPRIORITYCLASS:
		if (codeNotify == CBN_SELCHANGE) {
			SetPriorityClass(GetCurrentProcess(), (DWORD)
				ComboBox_GetItemData(hWndCtl, ComboBox_GetCurSel(hWndCtl)));
		}
		break;

	case IDC_THREADRELATIVEPRIORITY:
		if (codeNotify == CBN_SELCHANGE) {
			SetThreadPriority(GetCurrentThread(), (DWORD)
				ComboBox_GetItemData(hWndCtl, ComboBox_GetCurSel(hWndCtl)));
		}
		break;

	case IDC_SUSPEND:
		// To avoid deadlock, call EnableWindow before creating
		// the thread that calls SuspendThread.
		EnableWindow(hWndCtl, FALSE);

		HANDLE hThreadPrimary;
		DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
			GetCurrentProcess(), &hThreadPrimary,
			THREAD_SUSPEND_RESUME, FALSE, DUPLICATE_SAME_ACCESS);
		DWORD dwThreadID;
		CloseHandle(chBEGINTHREADEX(NULL, 0, ThreadFunc,
			hThreadPrimary, 0, &dwThreadID));
		break;
	}
}


///////////////////////////////////////////////////////////////////////////////


INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

	switch (uMsg) {
		chHANDLE_DLGMSG(hWnd, WM_INITDIALOG, Dlg_OnInitDialog);
		chHANDLE_DLGMSG(hWnd, WM_COMMAND, Dlg_OnCommand);
	}

	return(FALSE);
}


///////////////////////////////////////////////////////////////////////////////


int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int) {

	HWND hWnd =
		CreateDialog(hInstExe, MAKEINTRESOURCE(IDD_SCHEDLAB), NULL, Dlg_Proc);
	BOOL fQuit = FALSE;

	while (!fQuit) {
		MSG msg;
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {

			// IsDialogMessage allows keyboard navigation to work properly.
			if (!IsDialogMessage(hWnd, &msg)) {

				if (msg.message == WM_QUIT) {
					fQuit = TRUE;  // For WM_QUIT, terminate the loop.
				} else {
					// Not a WM_QUIT message. Translate it and dispatch it.
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
			}  // if (!IsDialogMessage())
		} else {

			// Add a number to the listbox
			static int s_n = -1;
			TCHAR sz[20];
			StringCchPrintf(sz, _countof(sz), TEXT("%u"), ++s_n);
			HWND hWndWork = GetDlgItem(hWnd, IDC_WORK);
			ListBox_SetCurSel(hWndWork, ListBox_AddString(hWndWork, sz));

			// Remove some strings if there are too many entries
			while (ListBox_GetCount(hWndWork) > 100)
				ListBox_DeleteString(hWndWork, 0);

			// How long should the thread sleep
			int nSleep = GetDlgItemInt(hWnd, IDC_SLEEPTIME, NULL, FALSE);
			if (chINRANGE(1, nSleep, 9999))
				Sleep(nSleep);
		}
	}

	DestroyWindow(hWnd);
	return(0);
}


//////////////////////////////// End of File //////////////////////////////////